﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using FTLiteLogic.Delegates;
using FTLiteLogic.Interface;
using FullThrustLogic.Core;
using FullThrustLogic.Core.GameState;
using FullThrustLogic.Helper;
using FullThrustLogic.ShipComponent.Weapon;

namespace FullThrustLogic.ShipComponent
{
    public class Ship: IReportableEvent
    {
        private int _turnAmount = 30;
        private GameContext _gameContext;

        private ReportableEvent _eventReporter;
        public ReportableEvent EventReporter
        {
            get { return _eventReporter; }
        }


        public Ship(GameContext gameContext) 
        {
            _gameContext = gameContext;
            Thrusts = new List<Thrust>();
            Weapons = new List<iWeapon>();
            FireControlSystems = new List<FireControlSystem>();
            PriorMoves = new List<MovementOrder>();
            PriorPositions = new List<double[]>();
            Hull = new Hull();

            AvailableToFire = true;

            _eventReporter = new ReportableEvent();
        }


        public string Name { get; set; }

        public int Size
        {
            get;
            set;
        }

        public double Heading
        {
            get; set;
        }

        public double PositionX
        {
            get;
            set;
        }

        public double PositionY
        {
            get;
            set;
        }

        public double HeadingAfterMoveOrder
        {
            get;
            set;
        }

        public double PositionXAfterMoveOrder
        {
            get;
            set;
        }

        public double PositionYAfterMoveOrder
        {
            get;
            set;
        }

        public Hull Hull
        {
            get;
            set;
        }

        public List<Thrust> Thrusts
        {
            get;
            set;
        }

        public List<FireControlSystem> FireControlSystems
        {
            get;
            set;
        }

        public List<iWeapon> Weapons
        {
            get;
            set;
        }

        public bool Enabled
        {
            get;
            set;
        }

        public bool AvailableToFire
        {
            get;
            set;
        }

        public MovementOrder CurrentMovementOrder
        {
            get;
            set;
        }

        public Player OwnerPlayer
        {
            get;
            set;
        }

        public List<MovementOrder> PriorMoves
        {
            get;
            set;
        }

        public int MaxNumberOfPriorMoves
        {
            get;
            set;
        }

        public bool ShouldRunSystemCheck
        {
            get;
            set;
        }

        public int VelocityAfterMoveOrder
        {
            get;
            set;
        }

        public int Velocity
        {
            get;
            set;
        }


        public List<double[]> PriorPositions
        {
            get;
            set;
        }

        public List<Ship> EnemyShipsInRange
        {
            get
            {
                List<Ship> shipList = new List<Ship>();

                foreach (iWeapon weapon in Weapons)
                {
                    if (weapon.Enabled)
                    {
                        if (_gameContext.CurrentState is AttackOrderState)
                        {
                            foreach(Ship ship in ((AttackOrderState)_gameContext.CurrentState).GetShipsInRange(this, weapon))
                            {
                                if(!shipList.Contains(ship))
                                {
                                    shipList.Add(ship);
                                }
                            }
                        }
                    }
                }

                return shipList;
            }
        }

        public bool HasEnemyShipsInRange
        {
            get
            {
                foreach(iWeapon weapon in Weapons)
                {
                    if(weapon.Enabled)
                    {
                        if(_gameContext.CurrentState is AttackOrderState)
                        {
                            if(((AttackOrderState)_gameContext.CurrentState).GetShipsInRange(this, weapon).Count >0)
                            {
                                return true;
                            }
                        }
                    }
                }
                return false;
            }
        }



        public void ApplyDamage(int damageAmount)
        {
            for(int i =0; i < damageAmount; i++)
            {
                Hull.Stregth[Hull.ThresholdLevel].CurrentDamage++;
                if(Hull.Stregth[Hull.ThresholdLevel].CurrentDamage >= Hull.Stregth[Hull.ThresholdLevel].MaxDamage)
                {
                    ShouldRunSystemCheck = true;

                    Hull.ThresholdLevel++;

                    if(Hull.ThresholdLevel > Hull.MaxThresholdLevel)
                    {
                        Enabled = false;
                    }
                }
            }

            if(ShouldRunSystemCheck)
            {
                RunSystemCheck();
            }
        }

        public void RunSystemCheck()
        {
            StringBuilder eventDescription = new StringBuilder();

            eventDescription.AppendLine(@"Damage Caused System Check!");

            foreach(iWeapon weapon in Weapons)
            {
                weapon.Enabled = DiceHelper.DetermineSuccess(Hull.SystemCheckLevel, 1);

                if (!weapon.Enabled)
                {
                    eventDescription.AppendLine(@"Weapon Disabled!");
                }
            }

            foreach (FireControlSystem fireControlSystem  in FireControlSystems)
            {
                fireControlSystem.Enabled = DiceHelper.DetermineSuccess(Hull.SystemCheckLevel, 1);

                if (!fireControlSystem.Enabled)
                {
                    eventDescription.AppendLine(@"Fire Control System Disabled!");
                }
            }

            foreach(Thrust thrust in Thrusts)
            {
                thrust.Enabled = DiceHelper.DetermineSuccess(Hull.SystemCheckLevel, 1);

                if (!thrust.Enabled)
                {
                    eventDescription.AppendLine(@"Thrust Disabled!");
                }
            }

            ShouldRunSystemCheck = false;
            
            _eventReporter.Report(this, new ReportableEventArgs() { EventDescription = eventDescription.ToString(), IsEventSuccess = true });
        }

        public void ApplyAttackOrders()
        {
            foreach(FireControlSystem fireControlSystem in FireControlSystems)
            {
                if (fireControlSystem.Enabled)
                {
                    foreach (AttackOrder attackOrder in fireControlSystem.AttackOrders)
                    {
                        attackOrder.ApplyOrder(this, fireControlSystem.TargetShip);
                    }
                }

                fireControlSystem.AttackOrders.Clear();
            }

            AvailableToFire = false;
        }

        public void ExecuteMovementOrder(bool ShouldCommitResult)
        {
            if (CurrentMovementOrder != null)
            {
                int newVelocity = this.Velocity;
                newVelocity += CurrentMovementOrder.VelocityAdjustment;
                
                int newTurnAmount = _turnAmount;
                               
                if (CurrentMovementOrder.Direction == Direction.Portside)
                {
                    newTurnAmount = _turnAmount * -1;
                }

                int velocityOne = (int)Math.Floor(newVelocity / 2.0);
                int velocityTwo = (int)Math.Ceiling(newVelocity / 2.0);

                int courseChangeOne = (int) Math.Floor(CurrentMovementOrder.TurnThrust/2.0);
                int courseChangeTwo = (int) Math.Ceiling(CurrentMovementOrder.TurnThrust/2.0);

                double headingOneAngle = this.Heading + ((newTurnAmount * courseChangeOne) % 360);
                double headingTwoAngle = this.Heading + ((newTurnAmount * courseChangeTwo) % 360);

               double newHeading = headingTwoAngle;

                LogicalPoint midLogicalPoint = XYHelper.MovePointByDistanceByDirection(new LogicalPoint(PositionX, PositionY), velocityOne, headingOneAngle);
                LogicalPoint finalLogicalPoint = XYHelper.MovePointByDistanceByDirection(new LogicalPoint(midLogicalPoint.X, midLogicalPoint.Y), velocityTwo, headingTwoAngle);

                if (ShouldCommitResult)
                {
                    PriorMoves.Insert(0, CurrentMovementOrder);
                    PriorPositions.Insert(0, new double[2] {PositionX, PositionY});

                    if (PriorMoves.Count > MaxNumberOfPriorMoves)
                    {
                        for (int i = 0; i > MaxNumberOfPriorMoves; i--)
                        {
                            PriorMoves.RemoveAt(i);
                            PriorPositions.RemoveAt(i);
                        }
                    }

                    PositionX = finalLogicalPoint.X;
                    PositionY = finalLogicalPoint.Y;

                    this.Heading = newHeading;
                    this.Velocity = newVelocity;
                }
                else
                {
                   PositionXAfterMoveOrder = finalLogicalPoint.X;
                    PositionYAfterMoveOrder = finalLogicalPoint.Y;

                    HeadingAfterMoveOrder = newHeading;
                    VelocityAfterMoveOrder= newVelocity; 
                }
            }
            else
            {
                int newVelocity = this.Velocity;
              
                LogicalPoint finalLogicalPoint = XYHelper.MovePointByDistanceByDirection(new LogicalPoint(PositionX, PositionY), Velocity, Heading);

                if (ShouldCommitResult)
                {
                    PriorMoves.Insert(0, CurrentMovementOrder);
                    PriorPositions.Insert(0, new double[2] { PositionX, PositionY });

                    if (PriorMoves.Count > MaxNumberOfPriorMoves)
                    {
                        for (int i = 0; i > MaxNumberOfPriorMoves; i--)
                        {
                            PriorMoves.RemoveAt(i);
                            PriorPositions.RemoveAt(i);
                        }
                    }

                    PositionX = finalLogicalPoint.X;
                    PositionY = finalLogicalPoint.Y;

                    this.Velocity = newVelocity;
                }
                else
                {
                    PositionXAfterMoveOrder = finalLogicalPoint.X;
                    PositionYAfterMoveOrder = finalLogicalPoint.Y;

                    VelocityAfterMoveOrder = newVelocity;

                    HeadingAfterMoveOrder = this.Heading;
                }
            }

            if (ShouldCommitResult)
            {
                CurrentMovementOrder = null;
            }
        }


    }
}

